home *** CD-ROM | disk | FTP | other *** search
/ Games of Daze / Infomagic - Games of Daze (Summer 1995) (Disc 1 of 2).iso / gnuish / dif115as / io.c < prev    next >
C/C++ Source or Header  |  1992-02-22  |  23KB  |  762 lines

  1. /* File I/O for GNU DIFF.
  2.    Copyright (C) 1988, 1989 Free Software Foundation, Inc.
  3.  
  4. This file is part of GNU DIFF.
  5.  
  6. GNU DIFF is free software; you can redistribute it and/or modify
  7. it under the terms of the GNU General Public License as published by
  8. the Free Software Foundation; either version 1, or (at your option)
  9. any later version.
  10.  
  11. GNU DIFF is distributed in the hope that it will be useful,
  12. but WITHOUT ANY WARRANTY; without even the implied warranty of
  13. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  14. GNU General Public License for more details.
  15.  
  16. You should have received a copy of the GNU General Public License
  17. along with GNU DIFF; see the file COPYING.  If not, write to
  18. the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.  */
  19.  
  20. /* MS-DOS port (c) 1990 by Thorsten Ohl, ohl@gnu.ai.mit.edu
  21. This port is also distributed under the terms of the GNU General
  22. Public License as published by the Free Software Foundation.
  23.  
  24. Please note that this file is not identical to the original GNU release,
  25. you should have received this code as patch to the official release.
  26.  
  27. $Header: e:/gnu/diff/RCS/io.c 1.15.0.2 91/03/12 17:06:38 tho Exp $  */
  28.  
  29. #include "diff.h"
  30.  
  31. #ifdef __STDC__
  32. static  int binary_file_p(char *, int);
  33. static  int slurp(void);
  34. static  void find_identical_ends(struct file_data *);
  35. static  void find_and_hash_each_line(void);
  36. static  int find_equiv_class(int);
  37. #endif /* __STDC__ */
  38.  
  39. /* Rotate a value n bits to the left. */
  40. #define UINT_BIT (sizeof (unsigned) * CHAR_BIT)
  41. #define ROL(v, n) ((v) << (n) | (v) >> UINT_BIT - (n))
  42.  
  43. /* Given a hash value and a new character, return a new hash value. */
  44. #define HASH(h, c) ((c) + ROL (h, 7))
  45.  
  46. /* Current file under consideration. */
  47. struct file_data *current;
  48.  
  49. /* Check for binary files and compare them for exact identity.  */
  50.  
  51. /* Return 1 if BUF contains a non text character.
  52.    SIZE is the number of characters in BUF.  */
  53.  
  54. static int
  55. binary_file_p (buf, size)
  56.      char *buf;
  57.      int size;
  58. {
  59.   static const char textchar[] = {
  60.     /* ISO 8859 */
  61.     0, 0, 0, 0, 0, 0, 0, 0,
  62.     0, 1, 1, 1, 1, 1, 0, 0,
  63.     0, 0, 0, 0, 0, 0, 0, 0,
  64.     0, 0, 0, 0, 0, 0, 0, 0,
  65.     1, 1, 1, 1, 1, 1, 1, 1,
  66.     1, 1, 1, 1, 1, 1, 1, 1,
  67.     1, 1, 1, 1, 1, 1, 1, 1,
  68.     1, 1, 1, 1, 1, 1, 1, 1,
  69.     1, 1, 1, 1, 1, 1, 1, 1,
  70.     1, 1, 1, 1, 1, 1, 1, 1,
  71.     1, 1, 1, 1, 1, 1, 1, 1,
  72.     1, 1, 1, 1, 1, 1, 1, 1,
  73.     1, 1, 1, 1, 1, 1, 1, 1,
  74.     1, 1, 1, 1, 1, 1, 1, 1,
  75.     1, 1, 1, 1, 1, 1, 1, 1,
  76.     1, 1, 1, 1, 1, 1, 1, 0,
  77.     0, 0, 0, 0, 0, 0, 0, 0,
  78.     0, 0, 0, 0, 0, 0, 0, 0,
  79.     0, 0, 0, 0, 0, 0, 0, 0,
  80.     0, 0, 0, 0, 0, 0, 0, 0,
  81.     1, 1, 1, 1, 1, 1, 1, 1,
  82.     1, 1, 1, 1, 1, 1, 1, 1,
  83.     1, 1, 1, 1, 1, 1, 1, 1,
  84.     1, 1, 1, 1, 1, 1, 1, 1,
  85.     1, 1, 1, 1, 1, 1, 1, 1,
  86.     1, 1, 1, 1, 1, 1, 1, 1,
  87.     1, 1, 1, 1, 1, 1, 1, 1,
  88.     1, 1, 1, 1, 1, 1, 1, 1,
  89.     1, 1, 1, 1, 1, 1, 1, 1,
  90.     1, 1, 1, 1, 1, 1, 1, 1,
  91.     1, 1, 1, 1, 1, 1, 1, 1,
  92.     1, 1, 1, 1, 1, 1, 1, 1,
  93.   };
  94.   while (--size >= 0)
  95.     if (!textchar[*buf++ & 0377])
  96.       return 1;
  97.   return 0;
  98. }
  99.  
  100. int binary_file_threshold = 512;
  101.  
  102. /* Slurp the current file completely into core.
  103.    Return nonzero if it appears to be a binary file.  */
  104.  
  105. static int
  106. slurp ()
  107. {
  108.   /* If we have a nonexistent file at this stage, treat it as empty.  */
  109.   if (current->desc < 0)
  110.     {
  111.       current->bufsize = 0;
  112.       current->buffered_chars = 0;
  113.       current->buffer = 0;
  114.     }
  115.   /* If it's a regular file, we can just get the size out of the stat
  116.      block and slurp it in all at once. */
  117.   /* In all cases, we leave room in the buffer for 2 extra chars
  118.      beyond those that current->bufsize describes:
  119.      one for a newline (in case the text does not end with one)
  120.      and one for a sentinel in find_identical_ends.  */
  121.   else if ((current->stat.st_mode & S_IFMT) == S_IFREG)
  122.     {
  123.       current->bufsize = current->stat.st_size;
  124.  
  125. #ifdef MSDOS
  126.       current->buffer = (char _huge *) xhalloc (current->bufsize + 2L);
  127.       current->buffered_chars
  128.     = hread (current->desc, current->buffer, current->bufsize);
  129. #else /* not MSDOS */
  130.       current->buffer = (char *) xmalloc (current->bufsize + 2);
  131.       current->buffered_chars
  132.     = read (current->desc, current->buffer, current->bufsize);
  133. #endif /* not MSDOS */
  134.       if (current->buffered_chars < 0)
  135.     pfatal_with_name (current->name);
  136.     }
  137.   else
  138.     {
  139. #ifdef MSDOS
  140.       long cc;
  141.  
  142.       current->bufsize = 0x2000L;
  143.       current->buffer = (char _huge *) xhalloc (current->bufsize + 2L);
  144.       current->buffered_chars = 0L;
  145.  
  146.       /* Not a regular file; read it in a little at a time, growing the
  147.      buffer as necessary.
  148.      MS-DOS: This is really slow size we do not double
  149.      the BUFSIZE on each step, we rather increase it linearly.
  150.      I think this greatly inproves the changes of managing a 
  151.      tight fit.  */
  152.       while ((cc = hread (current->desc,
  153.                current->buffer + current->buffered_chars,
  154.                current->bufsize - current->buffered_chars))
  155.           > 0L)
  156.     {
  157.       current->buffered_chars += cc;
  158.       if (current->buffered_chars == current->bufsize)
  159.         {
  160.           current->buffer
  161.         = (char _huge *) xhrealloc (current->buffer,
  162.                        current->bufsize + 0x2002L,
  163.                        current->bufsize);
  164.           current->bufsize = current->bufsize + 0x2000L;
  165.         }
  166.     }
  167.  
  168.       if (cc < 0L)
  169.     pfatal_with_name (current->name);
  170.  
  171. #else /* not MSDOS */
  172.  
  173.       int cc;
  174.  
  175.       current->bufsize = 4096;
  176.       current->buffer = (char *) xmalloc (current->bufsize + 2);
  177.       current->buffered_chars = 0;
  178.  
  179.       /* Not a regular file; read it in a little at a time, growing the
  180.      buffer as necessary. */
  181.       while ((cc = read (current->desc,
  182.              current->buffer + current->buffered_chars,
  183.              current->bufsize - current->buffered_chars))
  184.          > 0)
  185.     {
  186.       current->buffered_chars += cc;
  187.       if (current->buffered_chars == current->bufsize)
  188.         {
  189.           current->bufsize = current->bufsize * 2;
  190.           current->buffer = (char *) xrealloc (current->buffer,
  191.                            current->bufsize + 2);
  192.         }
  193.     }
  194.  
  195.       if (cc < 0)
  196.     pfatal_with_name (current->name);
  197.  
  198. #endif /* not MSDOS */
  199.     }
  200.  
  201.   /* Check first part of file to see if it's a binary file.  */
  202.   if (! always_text_flag
  203.       && binary_file_p (current->buffer,
  204. #ifdef MSDOS
  205.        min ((int) current->buffered_chars, binary_file_threshold)))
  206. #else
  207.             min (current->buffered_chars, binary_file_threshold)))
  208. #endif
  209.     return 1;
  210.  
  211.   /* If not binary, make sure text ends in a newline,
  212.      but remember that we had to add one unless -B is in effect.  */
  213.   if (current->buffered_chars > 0
  214.       && current->buffer[current->buffered_chars - 1] != '\n')
  215.     {
  216.       current->missing_newline = !ignore_blank_lines_flag;
  217.       current->buffer[current->buffered_chars++] = '\n';
  218.     }
  219.   else
  220.     current->missing_newline = 0;
  221.  
  222.   /* Don't use uninitialized storage. */
  223.   if (current->buffer != 0)
  224.     current->buffer[current->buffered_chars] = '\0';
  225.  
  226.   return 0;
  227. }
  228.  
  229. /* Split the file into lines, simultaneously computing the hash codes for
  230.    each line. */
  231.  
  232. void
  233. find_and_hash_each_line ()
  234. {
  235.   unsigned h;
  236.   int i;
  237.   unsigned char _huge *p = (unsigned char _huge *) current->prefix_end;
  238.   unsigned char _huge *ip, c;
  239.  
  240.   /* Attempt to get a good initial guess as to the number of lines. */
  241. #ifdef MSDOS
  242.   current->linbufsize = (int) (current->buffered_chars / 50) + 5;
  243. #else
  244.   current->linbufsize = current->buffered_chars / 50 + 5;
  245. #endif
  246.   current->linbuf
  247.     = (struct line_def *) xmalloc (current->linbufsize * sizeof (struct line_def));
  248.  
  249.   if (function_regexp || output_style == OUTPUT_IFDEF)
  250.     {
  251.       /* If the -C, -D or -F option is used, we need to find the lines
  252.      of the matching prefix.  At least we will need to find the last few,
  253.      but since we don't know how many, it's easiest to find them all.
  254.      If -D is specified, we need all the lines of the first file.  */
  255.       current->buffered_lines = 0;
  256.       p = (unsigned char _huge *) current->buffer;
  257.     }
  258.   else
  259.     {
  260.       /* Skip the identical prefixes, except be prepared to handle context.
  261.      In fact, handle 1 more preceding line than the context says,
  262.      in case shift_boundaries moves things backwards in this file.  */
  263.       current->buffered_lines = current->prefix_lines - context - 1;
  264.       if (current->buffered_lines < 0)
  265.     current->buffered_lines = 0;
  266.       for (i = 0; i < context + 1; ++i)
  267.     /* Unless we are at the beginning, */
  268.     if ((char _huge *) p != current->buffer)
  269.       /* Back up at least 1 char until at the start of a line.  */
  270.       while ((char _huge *) --p != current->buffer && p[-1] != '\n')
  271.         ;
  272.     }
  273.  
  274.   while ((char _huge *) p < current->suffix_begin)
  275.     {
  276.       h = 0;
  277.       ip = p;
  278.  
  279.       if (current->prefix_end <= (char _huge *) p)
  280.     {
  281.       /* Hash this line until we find a newline. */
  282.       if (ignore_case_flag)
  283.         {
  284.           if (ignore_all_space_flag)
  285.         while ((c = *p) != '\n')
  286.           {
  287.             if (! isspace (c))
  288.               if (isupper (c))
  289.             h = HASH (h, tolower (c));
  290.               else
  291.             h = HASH (h, c);
  292.             ++p;
  293.           }
  294.           else if (ignore_space_change_flag)
  295.  
  296.         while ((c = *p) != '\n')
  297.           {
  298.             if (c == ' ' || c == '\t')
  299.               {
  300.             while ((c = *p) == ' ' || c == '\t')
  301.               ++p;
  302.             if (c == '\n')
  303.               break;
  304.             h = HASH (h, ' ');
  305.               }
  306.             /* C is now the first non-space.  */
  307.             if (isupper (c))
  308.               h = HASH (h, tolower (c));
  309.             else
  310.               h = HASH (h, c);
  311.             ++p;
  312.           }
  313.           else
  314.         while ((c = *p) != '\n')
  315.           {
  316.             if (isupper (c))
  317.               h = HASH (h, tolower (c));
  318.             else
  319.               h = HASH (h, c);
  320.             ++p;
  321.           }
  322.         }
  323.       else
  324.         {
  325.           if (ignore_all_space_flag)
  326.         while ((c = *p) != '\n')
  327.           {
  328.             if (! isspace (c))
  329.               h = HASH (h, c);
  330.             ++p;
  331.           }
  332.           else if (ignore_space_change_flag)
  333.         while ((c = *p) != '\n')
  334.           {
  335.             if (c == ' ' || c == '\t')
  336.               {
  337.             while ((c = *p) == ' ' || c == '\t')
  338.               ++p;
  339.             if (c == '\n')
  340.               break;
  341.             h = HASH (h, ' ');
  342.               }
  343.             /* C is not the first non-space.  */
  344.             h = HASH (h, c);
  345.             ++p;
  346.           }
  347.           else
  348.         while ((c = *p) != '\n')
  349.           {
  350.             h = HASH (h, c);
  351.             ++p;
  352.           }
  353.         }
  354.     }
  355.       else
  356.     /* This line is part of the matching prefix,
  357.        so we don't need to hash it.  */
  358.     while (*p != '\n')
  359.       ++p;
  360.       
  361.       /* Maybe increase the size of the line table. */
  362.       if (current->buffered_lines >= current->linbufsize)
  363.     {
  364.       while (current->buffered_lines >= current->linbufsize)
  365. #ifdef MSDOS            /* don't be to generous!  */
  366.         {
  367.           current->linbufsize += 0x0100;
  368.           if (current->linbufsize >= 0xffe0 / sizeof (struct line_def))
  369.         fatal ("to many lines in input");
  370.         }
  371. #else /* not MSDOS */
  372.         current->linbufsize *= 2;
  373. #endif /* not MSDOS */
  374.       current->linbuf
  375.         = (struct line_def *) xrealloc (current->linbuf,
  376.                         current->linbufsize
  377.                         * sizeof (struct line_def));
  378.     }
  379.       current->linbuf[current->buffered_lines].text = (char _huge *) ip;
  380. #ifdef MSDOS            /* Be explicit to the compiler!  */
  381.       current->linbuf[current->buffered_lines].length
  382.     = (int) ((long) (p - ip) + 1);
  383. #else /* not MSDOS */
  384.       current->linbuf[current->buffered_lines].length = p - ip + 1;
  385. #endif /* not MSDOS */
  386.       current->linbuf[current->buffered_lines].hash = h;
  387.       ++current->buffered_lines;
  388.       ++p;
  389.     }
  390.  
  391.   i = 0;
  392.   while ((i < context || output_style == OUTPUT_IFDEF)
  393.      && (char _huge *) p < current->buffer + current->buffered_chars)
  394.     {
  395.       ip = p;
  396.       while (*p++ != '\n')
  397.     ;
  398.       /* Maybe increase the size of the line table. */
  399.       if (current->buffered_lines >= current->linbufsize)
  400.     {
  401.       while (current->buffered_lines >= current->linbufsize)
  402. #ifdef MSDOS            /* don't be to generous!  */
  403.         {
  404.           current->linbufsize += 0x0100;
  405.           if (current->linbufsize >= 0xffe0 / sizeof (struct line_def))
  406.         fatal ("to many lines in input");
  407.         }
  408. #else /* not MSDOS */
  409.         current->linbufsize *= 2;
  410. #endif /* not MSDOS */
  411.       current->linbuf
  412.         = (struct line_def *) xrealloc (current->linbuf,
  413.                         current->linbufsize
  414.                         * sizeof (struct line_def));
  415.     }
  416.       current->linbuf[current->buffered_lines].text = (char _huge *) ip;
  417. #ifdef MSDOS        /* Be explicit to the compiler!  */
  418.       current->linbuf[current->buffered_lines].length
  419.     = (int) ((long) (p - ip));
  420. #else /* not MSDOS */
  421.       current->linbuf[current->buffered_lines].length = p - ip;
  422. #endif /* not MSDOS */
  423.       current->linbuf[current->buffered_lines].hash = 0;
  424.       ++current->buffered_lines;
  425.       ++i;
  426.     }
  427.  
  428.   if (ROBUST_OUTPUT_STYLE (output_style)
  429.       && current->missing_newline
  430.       && current->suffix_begin == current->buffer + current->buffered_chars)
  431.     --current->linbuf[current->buffered_lines - 1].length;
  432. }
  433.  
  434. /* Given a vector of two file_data objects, find the identical
  435.    prefixes and suffixes of each object. */
  436.  
  437. static void
  438. find_identical_ends (filevec)
  439.      struct file_data filevec[];
  440. {
  441.   char _huge *p0, _huge *p1, _huge *end0, _huge *beg0;
  442.   int lines;
  443.  
  444.   if (filevec[0].buffered_chars == 0 || filevec[1].buffered_chars == 0)
  445.     {
  446.       filevec[0].prefix_end = filevec[0].buffer;
  447.       filevec[1].prefix_end = filevec[1].buffer;
  448.       filevec[0].prefix_lines = filevec[1].prefix_lines = 0;
  449.       filevec[0].suffix_begin = filevec[0].buffer + filevec[0].buffered_chars;
  450.       filevec[1].suffix_begin = filevec[1].buffer + filevec[1].buffered_chars;
  451.       filevec[0].suffix_lines = filevec[1].suffix_lines = 0;
  452.       return;
  453.     }
  454.  
  455.   /* Find identical prefix.  */
  456.  
  457.   p0 = filevec[0].buffer;
  458.   p1 = filevec[1].buffer;
  459.   lines = 0;
  460.  
  461.   /* Insert end "sentinels", in this case characters that are guaranteed
  462.      to make the equality test false, and thus terminate the loop.  */
  463.  
  464.   if (filevec[0].buffered_chars < filevec[1].buffered_chars)
  465.     p0[filevec[0].buffered_chars] = ~p1[filevec[0].buffered_chars];
  466.   else
  467.     p1[filevec[1].buffered_chars] = ~p0[filevec[1].buffered_chars];
  468.  
  469.   /* Loop until first mismatch, or to the sentinel characters.  */
  470.   while (1)
  471.     {
  472.       char c = *p0++;
  473.       if (c != *p1++)
  474.     break;
  475.       if (c == '\n')
  476.     ++lines;
  477.     }
  478.  
  479.   /* Don't count missing newline as part of prefix in RCS mode. */
  480.   if (ROBUST_OUTPUT_STYLE (output_style)
  481. #ifdef MSDOS
  482.       && ((filevec[0].missing_newline
  483.        && (long) (p0 - filevec[0].buffer)
  484.          > (long) filevec[0].buffered_chars)
  485.       ||
  486.       (filevec[1].missing_newline
  487.        && (long) (p1 - filevec[1].buffer)
  488.          > (long) filevec[1].buffered_chars)))
  489. #else /* not MSDOS */
  490.       && ((filevec[0].missing_newline
  491.        && p0 - filevec[0].buffer > filevec[0].buffered_chars)
  492.       ||
  493.       (filevec[1].missing_newline
  494.        && p1 - filevec[1].buffer > filevec[1].buffered_chars)))
  495. #endif /* not MSDOS */
  496.     --p0, --p1, --lines;
  497.  
  498.   /* If the sentinel was passed, and lengths are equal, the
  499.      files are identical.  */
  500. #ifdef MSDOS
  501.   if ((long) (p0 - filevec[0].buffer) > (long) filevec[0].buffered_chars
  502. #else /* not MSDOS */
  503.   if (p0 - filevec[0].buffer > filevec[0].buffered_chars
  504. #endif /* not MSDOS */
  505.       && filevec[0].buffered_chars == filevec[1].buffered_chars)
  506.     {
  507.       filevec[0].prefix_end = p0 - 1;
  508.       filevec[1].prefix_end = p1 - 1;
  509.       filevec[0].prefix_lines = filevec[1].prefix_lines = lines;
  510.       filevec[0].suffix_begin = filevec[0].buffer;
  511.       filevec[1].suffix_begin = filevec[1].buffer;
  512.       filevec[0].suffix_lines = filevec[1].suffix_lines = lines;
  513.       return;
  514.     }
  515.  
  516.   /* Point at first nonmatching characters.  */
  517.   --p0, --p1;
  518.  
  519.   /* Skip back to last line-beginning in the prefix.  */
  520.   while (p0 != filevec[0].buffer && p0[-1] != '\n')
  521.     --p0, --p1;
  522.  
  523.   /* Record the prefix.  */
  524.   filevec[0].prefix_end = p0;
  525.   filevec[1].prefix_end = p1;
  526.   filevec[0].prefix_lines = filevec[1].prefix_lines = lines;
  527.   
  528.   /* Find identical suffix.  */
  529.  
  530.   /* P0 and P1 point beyond the last chars not yet compared.  */
  531.   p0 = filevec[0].buffer + filevec[0].buffered_chars;
  532.   p1 = filevec[1].buffer + filevec[1].buffered_chars;
  533.   lines = 0;
  534.  
  535.   if (! ROBUST_OUTPUT_STYLE (output_style)
  536.       || filevec[0].missing_newline == filevec[1].missing_newline)
  537.     {
  538.       end0 = p0;        /* Addr of last char in file 0.  */
  539.  
  540.       /* Get value of P0 at which we should stop scanning backward:
  541.      this is when either P0 or P1 points just past the last char
  542.      of the identical prefix.  */
  543.       if (filevec[0].buffered_chars < filevec[1].buffered_chars)
  544.     beg0 = filevec[0].prefix_end;
  545.       else
  546.     /* Figure out where P0 will be when P1 is at the end of the prefix.
  547.        Thus we only need to test P0.  */
  548.     beg0 = (filevec[0].prefix_end
  549.         + filevec[0].buffered_chars - filevec[1].buffered_chars);
  550.  
  551.       /* Scan back until chars don't match or we reach that point.  */
  552.       while (p0 != beg0)
  553.     {
  554.       char c = *--p0;
  555.       if (c != *--p1)
  556.         {
  557.           /* Point at the first char of the matching suffix.  */
  558.           ++p0, ++p1;
  559.           break;
  560.         }
  561.       if (c == '\n')
  562.         ++lines;
  563.     }
  564.  
  565.       /* Are we at a line-beginning in both files?  */
  566.       if (p0 != end0
  567.       && !((p0 == filevec[0].buffer || p0[-1] == '\n')
  568.            &&
  569.            (p1 == filevec[1].buffer || p1[-1] == '\n')))
  570.     {
  571.       /* No.  We counted one line too many.  */
  572.       --lines;
  573.       /* Advance to next place that is a line-beginning in both files.  */
  574.       do
  575.         {
  576.           ++p0, ++p1;
  577.         }
  578.       while (p0 != end0 && p0[-1] != '\n');
  579.     }
  580.     }
  581.  
  582.   /* Record the suffix.  */
  583.   filevec[0].suffix_begin = p0;
  584.   filevec[1].suffix_begin = p1;
  585.   filevec[0].suffix_lines = filevec[1].suffix_lines = lines;
  586. }
  587.  
  588. /* Lines are put into equivalence classes (of lines that match in line_cmp).
  589.    Each equivalence class is represented by one of these structures,
  590.    but only while the classes are being computed.
  591.    Afterward, each class is represented by a number.  */
  592. struct equivclass
  593. {
  594.   struct equivclass *next;    /* Next item in this bucket. */
  595.   struct line_def line;    /* A line that fits this class. */
  596. };
  597.  
  598. /* Hash-table: array of buckets, each being a chain of equivalence classes.  */
  599. static struct equivclass **buckets;
  600.   
  601. /* Size of the bucket array. */
  602. static int nbuckets;
  603.  
  604. /* Array in which the equivalence classes are allocated.
  605.    The bucket-chains go through the elements in this array.
  606.    The number of an equivalence class is its index in this array.  */
  607. static struct equivclass *equivs;
  608.  
  609. /* Index of first free element in the array `equivs'.  */
  610. static int equivs_index;
  611.  
  612. /* Size allocated to the array `equivs'.  */
  613. static int equivs_alloc;
  614.  
  615. /* Largest primes less than some power of two, for nbuckets.  Values range
  616.    from useful to preposterous.  If one of these numbers isn't prime
  617.    after all, don't blame it on me, blame it on primes (6) . . . */
  618. static int primes[] =
  619. {
  620.   509,
  621.   1021,
  622.   2039,
  623.   4093,
  624.   8191,
  625.   16381,
  626.   32749,
  627.   65521,
  628. #ifndef MSDOS
  629.   131071,
  630.   262139,
  631.   524287,
  632.   1048573,
  633.   2097143,
  634.   4194301,
  635.   8388593,
  636.   16777213,
  637.   33554393,
  638.   67108859,            /* Preposterously large . . . */
  639. #endif /* not MSDOS */
  640.   -1
  641. };
  642.  
  643. /* Index of current nbuckets in primes. */
  644. static int primes_index;
  645.  
  646. /* Find the equiv class associated with line N of the current file.  */
  647.  
  648. static int
  649. find_equiv_class (n)
  650.      int n;
  651. {
  652.   int bucket;
  653.   struct equivclass *b, *p = NULL;
  654.  
  655.   /* Equivalence class 0 is permanently allocated to lines that were
  656.      not hashed because they were parts of identical prefixes or
  657.      suffixes. */
  658.   if (n < current->prefix_lines
  659.       || current->linbuf[n].text >= current->suffix_begin)
  660.     return 0;
  661.  
  662.   /* Check through the appropriate bucket to see if there isn't already
  663.      an equivalence class for this line. */
  664.   bucket = current->linbuf[n].hash % nbuckets;
  665.   b = buckets[bucket];
  666.   while (b)
  667.     {
  668.       if (b->line.hash == current->linbuf[n].hash
  669.       && (b->line.length == current->linbuf[n].length
  670.           /* Lines of different lengths can match with certain options.  */
  671.           || length_varies)
  672.       && !line_cmp (&b->line, ¤t->linbuf[n]))
  673.     return b - equivs;
  674.       p = b, b = b->next;
  675.     }
  676.  
  677.   /* Create a new equivalence class in this bucket. */
  678.  
  679. #ifdef MSDOS
  680.   if (equivs_index >= equivs_alloc)
  681.     fatal ("too many differences, hash table overflow");
  682. #endif /* MSDOS */
  683.  
  684.   p = &equivs[equivs_index++];
  685.   p->next = buckets[bucket];
  686.   buckets[bucket] = p;
  687.   p->line = current->linbuf[n];
  688.  
  689.   return equivs_index - 1;
  690. }
  691.  
  692. /* Given a vector of two file_data objects, read the file associated
  693.    with each one, and build the table of equivalence classes.
  694.    Return nonzero if either file appears to be a binary file.  */
  695.  
  696. int
  697. read_files (filevec)
  698.      struct file_data filevec[];
  699. {
  700.   int i, j;
  701.   int binary = 0;
  702.   int this_binary;
  703.  
  704.   current = &filevec[0];
  705.   binary = this_binary = slurp ();
  706.  
  707.   current = &filevec[1];
  708.   this_binary = slurp ();
  709.   if (binary || this_binary)
  710.     return 1;
  711.  
  712.   find_identical_ends (filevec);
  713.  
  714.   for (i = 0; i < 2; ++i)
  715.     {
  716.       current = &filevec[i];
  717.       find_and_hash_each_line ();
  718.     }
  719.  
  720. #ifdef MSDOS
  721.   /* This is NOT guaranteed to be enough space, but we will try anyway and
  722.      abort iff the hash table really overflows.  The strategy will help
  723.      us A LOT iff there are long matching pre- and suffixes (but the
  724.      user will have to wait longer for the bad news if we have to
  725.      abort ...).  */
  726.   equivs_alloc = min ((int) (0xffe0 / sizeof (struct equivclass)),
  727.         filevec[0].buffered_lines + filevec[1].buffered_lines + 1);
  728. #else /* not MSDOS */
  729.   /* This is guaranteed to be enough space.  */
  730.   equivs_alloc = filevec[0].buffered_lines + filevec[1].buffered_lines + 1;
  731. #endif /* not MSDOS */
  732.  
  733.   equivs = (struct equivclass *) xmalloc (equivs_alloc * sizeof (struct equivclass));
  734.   /* Equivalence class 0 is permanently safe for lines that were not
  735.      hashed.  Real equivalence classes start at 1. */
  736.   equivs_index = 1;
  737.   
  738.   primes_index = 0;
  739.   while (primes[primes_index] < equivs_alloc / 3)
  740.     primes_index++;
  741.  
  742.   buckets = (struct equivclass **) xmalloc (primes[primes_index] * sizeof (struct equivclass *));
  743.   bzero (buckets, primes[primes_index] * sizeof (struct equivclass *));
  744.   nbuckets = primes[primes_index];
  745.  
  746.   for (i = 0; i < 2; ++i)
  747.     {
  748.       current = &filevec[i];
  749.       current->equivs
  750.     = (int *) xmalloc (current->buffered_lines * sizeof (int));
  751.       for (j = 0; j < current->buffered_lines; ++j)
  752.     current->equivs[j] = find_equiv_class (j);
  753.     }
  754.  
  755.   filevec[0].equiv_max = filevec[1].equiv_max = equivs_index;
  756.  
  757.   free (equivs);
  758.   free (buckets);
  759.  
  760.   return 0;
  761. }
  762.